package cn.sh.library.gmwx.sparql.impl;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.ontoware.rdf2go.model.Model;
import org.ontoware.rdf2go.model.QueryResultTable;
import org.springframework.stereotype.Repository;

import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.vocabulary.RDFS;

import cn.sh.library.gmwx.annotation.GraphDefine;
import cn.sh.library.gmwx.base.Constant;
import cn.sh.library.gmwx.base.Namespace;
import cn.sh.library.gmwx.bean.InstanceSearchBean;
import cn.sh.library.gmwx.common.ModelManager;
import cn.sh.library.gmwx.config.AppConfig;
import cn.sh.library.gmwx.dao.impl.BaseDaoImpl;
import cn.sh.library.gmwx.dto.Pager;
import cn.sh.library.gmwx.dto.QueryResult;
import cn.sh.library.gmwx.sparql.InstanceSparql;
import cn.sh.library.gmwx.sparql.MergeParts;
import cn.sh.library.gmwx.sparql.RdfQuery;
import cn.sh.library.gmwx.sparql.SparqlExecution;
import cn.sh.library.gmwx.utils.RDFUtils;

@Repository
@GraphDefine(name = Constant.GRAPH_INSTANCE)
public class InstanceSparqlImpl extends BaseDaoImpl implements InstanceSparql {
	
    @Resource
    private AppConfig appConfig;

    @Override
    public List<Map<String, String>> getWorkInstances(String work_uri) {
        Model model_op = ModelManager.get(model);
        QueryResultTable rst = model_op.sparqlSelect(RdfQuery.RDF_QUERY_WORK_INSTANCES.toString(work_uri));
        List<Map<String, String>> results = RDFUtils.transformQueryResultTable(rst);

        for (int i=0; i<results.size(); i++) {
            String uri = ((Map) results.get(i)).get("uri").toString();
            ArrayList vs = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_IDS.toString(uri), "ids");
            if (vs.size() > 0) {
                ((Map) results.get(i)).put("ids", ((Map) vs.get(0)).get("ids").toString());
            } else {
                ((Map) results.get(i)).put("ids", "");
            }

            vs = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_NOTES.toString(uri), "notes");
            if (vs.size() > 0) {
                ((Map) results.get(i)).put("notes", ((Map) vs.get(0)).get("notes").toString());
            } else {
                ((Map) results.get(i)).put("notes", "");
            }
        }

        return results;
    }

    @Override
    public QueryResult<Map<String, String>> getWorkInstances(InstanceSearchBean search, String order, Pager pager) {
        // List<Map<String, String>> year_list = this.getYears(search.getStartYear(), search.getEndYear());
        String clause = MergeParts.instanceSearch(search);
        
      //排序 BY_CM
    	String sort = "";
    	if(StringUtils.isNotBlank(search.getTitleSort())) {
    		if("0".equals(search.getTitleSort())) {
    			sort += "asc (?label)";
    		}
    		if("1".equals(search.getTitleSort())) {
    			sort += "desc (?label)";
    		}
    	}
    	if(StringUtils.isNotBlank(search.getPersonSort())) {
    		if("0".equals(search.getPersonSort())) {
    			sort += "asc (?creator)";
    		}
    		if("1".equals(search.getPersonSort())) {
    			sort += "desc (?creator)";
    		}
    	}
        
        // clause = clause + MergeParts.instanceYearsSearch(year_list);
        QueryResult<Map<String, String>> result = new QueryResult<>();

        Long count = null;
        ArrayList results = new ArrayList();

        if (pager.getRowCount() == null) {
            pager.setRowCount(Long.parseLong("0"));
        }

        if (isSearchBlank(search)) {
//            System.out.println(pager.getRowCount());
            if (pager.getRowCount() == 0) {
                if (StringUtils.isNotBlank(search.getYear())) {
                    results = SparqlExecution.jQuery(getModel(Constant.GRAPH_TEMP), Namespace.getNsPrefixString() + "SELECT ?count WHERE {?s rdfs:comment ?count ; rdfs:label '" + search.getYear() + "' .}", "count");
                } else if (StringUtils.isNotBlank(search.getPublisherUri())) {
                    results = SparqlExecution.jQuery(getModel(Constant.GRAPH_TEMP), Namespace.getNsPrefixString() + "SELECT ?count WHERE {<" + search.getPublisherUri() + "> rdfs:comment ?count .}", "count");
                } else if (StringUtils.isNotBlank(search.getPlaceUri())) {
                    results = SparqlExecution.jQuery(getModel(Constant.GRAPH_TEMP), Namespace.getNsPrefixString() + "SELECT ?count WHERE {<" + search.getPlaceUri() + "> rdfs:comment ?count .}", "count");
                } else {
                    results = SparqlExecution.jQuery(getModel(Constant.GRAPH_TEMP), Namespace.getNsPrefixString() + "SELECT ?count WHERE {<http://data.library.sh.cn/instance/count> rdfs:comment ?count .}", "count");
                }

                count = Long.parseLong(((Map) results.get(0)).get("count").toString());
            } else {
                count = pager.getRowCount();
            }
        } else {
            if (pager.getRowCount() == 0) {
            	// String sql=RdfQuery.RDF_COUNT_INSTANCE_Q2.toString(clause);
                results = SparqlExecution.vQuery(graph, RdfQuery.RDF_COUNT_INSTANCE_Q.toString(clause), "count");
                count = Long.parseLong(RDFUtils.getValue(((Map) results.get(0)).get("count").toString()));
            } else {
                count = pager.getRowCount();
            }
        }

        result.setTotalrecord(count);

        if(count > 0) {
            results = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_Q.toString(clause, sort, pager.getStartIndex(), pager.getPageSize()), "uri");
            for (int i=0; i<results.size(); i++) {
                String uri = ((Map) results.get(i)).get("uri").toString();
                Map info = (Map) SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_INFO.toString(uri), "title",
                        "response", "provision", "dimensions", "page", "terms", "year", "hor", "series", "seresponse", "seuri", "ilabel", "personInfos", "workUri").get(0);
                ((Map) results.get(i)).putAll(info);
                
                ArrayList vs = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_IDS.toString(uri), "ids");
                if (vs.size() > 0) {
                    ((Map) results.get(i)).put("ids", ((Map) vs.get(0)).get("ids").toString());
                } else {
                    ((Map) results.get(i)).put("ids", "");
                }

                vs = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_NOTES.toString(uri), "notes");
                if (vs.size() > 0) {
                    ((Map) results.get(i)).put("notes", ((Map) vs.get(0)).get("notes").toString());
                } else {
                    ((Map) results.get(i)).put("notes", "");
                }

                vs = SparqlExecution.vQuery(getModel(Constant.GRAPH_ITEM), RdfQuery.RDF_QUERY_INSTANCE_SHELFS.toString(uri), "shelfs");
                if (vs.size() > 0) {
                    ((Map) results.get(i)).put("shelfs", ((Map) vs.get(0)).get("shelfs").toString());
                } else {
                    ((Map) results.get(i)).put("shelfs", "");
                }
            }
            result.setResultList(RDFUtils.transformListMap(results));
        }
        return result;
    }

    @Override
    public void countInstance() {
        ArrayList results = SparqlExecution.jQuery(getModel(Constant.GRAPH_UNIFY), RdfQuery.RDF_COUNT_INSTANCE_Q.toString(""), "count");

        String count = RDFUtils.getValue(((Map) results.get(0)).get("count").toString());

        com.hp.hpl.jena.rdf.model.Model model = getModel(Constant.GRAPH_TEMP);
        com.hp.hpl.jena.rdf.model.Resource res = model.createResource("http://data.library.sh.cn/instance/count");

        res.addProperty(RDFS.comment, RDFUtils.getValue(count));
    }

    @Override
    public void updateFacetInstance(String facet, String type) {
        //Remove
        SparqlExecution.update(graph, Namespace.getNsPrefixString() + " WITH <" + Constant.GRAPH_TEMP + "> DELETE {?s ?p ?o} WHERE {?s a " + type + " ; ?p ?o .}");

        //Statistics
        ArrayList results = null;
        if (facet.equals("bf:provisionActivity/bf:date")) {
            results = SparqlExecution.vQuery(graph, RdfQuery.RDF_COUNT_INSTANCE_FACET_YEAR.toString(facet), "count", "facet");

            for (int i=0; i<results.size(); i++) {
                Object yo = ((Map) results.get(i)).get("facet");
                String year = "";

                if (yo != null) {
                    year = ((Map) results.get(i)).get("facet").toString();
                } else {
                    year = "9999";
                }

                ((Map) results.get(i)).put("type", "http://www.w3.org/2006/time#Instant");
                ((Map) results.get(i)).put("uri", "http://data.library.sh.cn/resource/instance/" + year);
            }
        } else if (facet.equals("bf:provisionActivity/bf:place")) {
            results = SparqlExecution.vQuery(graph, RdfQuery.RDF_COUNT_INSTANCE_FACET_PLACE.toString(facet), "uri", "count");

            for (int i=0; i<results.size(); i++) {
                String uri = ((Map) results.get(i)).get("uri").toString();
                //Get remote place
                ArrayList labels = SparqlExecution.rQuery(appConfig.getLocal(), RdfQuery.RDF_QUERY_INSTANCE_REMOTE_PLACE.toString(appConfig.getRemote(), uri), "label");
                if (labels.size() > 0) {
                    ((Map) results.get(i)).put("facet", RDFUtils.getValue(((Map) labels.get(0)).get("label").toString()));
                } else {
                    ((Map) results.get(i)).put("facet", uri);
                    //System.out.println(uri);
                }

                ((Map) results.get(i)).put("type", "http://www.library.sh.cn/ontology/Place");
            }
        } else {
            results = SparqlExecution.vQuery(getModel(Constant.GRAPH_UNIFY), RdfQuery.RDF_COUNT_INSTANCE_FACET.toString(facet), "uri", "count", "facet", "type");
        }

//        com.hp.hpl.jena.rdf.model.Model model = ModelFactory.createDefaultModel();

        for (int i=0; i<results.size(); i++) {
            String uri = ((Map) results.get(i)).get("uri").toString();
            String count = RDFUtils.getValue(((Map) results.get(i)).get("count").toString());

            Object fo = ((Map)results.get(i)).get("facet");
            String facet_text = "";

            if (fo != null) {
                facet_text = fo.toString();
            } else {
                facet_text = "9999";
            }

//            String utype = ((Map)results.get(i)).get("type").toString();


//            com.hp.hpl.jena.rdf.model.Resource res = model.createResource(uri);
//            res.addProperty(RDFS.comment, RDFUtils.getValue(count))
//                    .addProperty(RDFS.label, facet_text)
//                    .addProperty(RDF.type, model.createResource(utype));

            String str = Namespace.getNsPrefixString() + "WITH <" + Constant.GRAPH_TEMP + "> " +
                    "INSERT DATA {" +
                    "   <" + uri + "> rdfs:label '" + facet_text + "' ; " +
                    "       rdfs:comment '" + count + "' ; " +
                    "       rdf:type " + type + " . }";
            SparqlExecution.update(graph, str);
            //System.out.println("Finished [" + type + " - " + (i+1) + "/" + results.size() + "]");
        }

//        try {
//            getModel(Constant.GRAPH_TEMP).add(model);
//        } finally {
//            model.close();
//        }
    }

    @Override
    public Map<String, String> getBibInfos(String instance_uri) {
        Model model_op = ModelManager.get(getModel(Constant.GRAPH_UNIFY));
        Map<String, String> info_map = new HashMap();
        info_map =  RDFUtils.transformQueryResultTable(model_op.sparqlSelect(RdfQuery.RDF_QUERY_INSTANCE_DETAIL.toString(instance_uri))).get(0);
        info_map.put("place", "");
        //Get remote place label
        if (StringUtils.isNotBlank(info_map.get("placeUri").toString()) 
        		// 过滤空节点  @author CM 2018-07-25
        		&& !info_map.get("placeUri").toString().startsWith("nodeID://")) {
            ArrayList labels = SparqlExecution.rQuery(appConfig.getLocal(), RdfQuery.RDF_QUERY_INSTANCE_REMOTE_PLACE.toString(appConfig.getRemote(), info_map.get("placeUri").toString()), "label");

            if (labels.size() > 0) {
                info_map.put("place", RDFUtils.getValue(((Map) labels.get(0)).get("label").toString()));
            } else {
                info_map.put("place", info_map.get("placeUri").toString());
            }
        }
        ArrayList results = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_IDS.toString(instance_uri), "ids");
        if (results.size() > 0) {
            info_map.put("ids", ((Map) results.get(0)).get("ids").toString());
        }

        results = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_SHELFS.toString(instance_uri), "shelfs");
        if (results.size() > 0) {
            info_map.put("shelfs", ((Map) results.get(0)).get("shelfs").toString());
        }

        results = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_NOTES.toString(instance_uri), "notes");
        if (results.size() > 0) {
            info_map.put("notes", ((Map) results.get(0)).get("notes").toString());
        }

        return info_map;
    }

    @Override
    public List<Map<String, Object>> getContributions(String instance_uri) {
        //Get contributions
        ArrayList results = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_CONTRIBUTIONS.toString(instance_uri), "uri", "role");
        for (int i=0; i<results.size(); i++) {
            String uri = ((Map) results.get(i)).get("uri").toString();
//            System.out.println(uri);
            //Get remote contribution's label
            //等待"人民规范库"更新作者的label属性
            ArrayList labels = SparqlExecution.rQuery(appConfig.getLocal(), RdfQuery.RDF_QUERY_REMOTE_PERSON_NAME.toString(appConfig.getRemote(), uri), "name");
            if (labels.size() > 0) {
                ((Map) results.get(i)).put("name", ((Map) labels.get(0)).get("name").toString());
            } else {
                // ((Map) results.get(i)).put("name", uri);
                ((Map) results.get(i)).put("name", "");
            }
        }

        return results;
    }

    @Override
    public List<Map<String, String>> getWorks(String instance_uri) {
        return RDFUtils.transformListMap(SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_WORKS_INFO.toString(instance_uri), "uri", "title", "creator"));
    }

    @Override
    public OutputStream instanceTempModel(String instance_uri) {
        OutputStream stream = new ByteArrayOutputStream();
        com.hp.hpl.jena.rdf.model.Model model = ModelFactory.createDefaultModel();

        //instance info
        model.add(SparqlExecution.construct(graph, RdfQuery.RDF_CONSTRUCT_TARGET_TRIPLES.toString(instance_uri)));

        //works info
        ArrayList works = SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_INSTANCE_WORKS.toString(instance_uri), "uri");
        for (int i=0; i<works.size(); i++) {
            String work_uri = ((Map) works.get(i)).get("uri").toString();
            model.add(SparqlExecution.construct(graph, RdfQuery.RDF_CONSTRUCT_TARGET_TRIPLES.toString(work_uri)));
        }

        //items info
        ArrayList items = SparqlExecution.vQuery(getModel(Constant.GRAPH_ITEM), RdfQuery.RDF_QUERY_INSTANCE_ITEMS.toString(instance_uri), "uri");
        for (int i=0; i<items.size(); i++) {
            String item_uri = ((Map) items.get(i)).get("uri").toString();
            model.add(SparqlExecution.construct(graph, RdfQuery.RDF_CONSTRUCT_TARGET_TRIPLES.toString(item_uri)));
        }

        model.setNsPrefixes(getModel(Constant.GRAPH_WORK).getNsPrefixMap());
        model.write(stream, "RDF/XML-ABBREV");

        return stream;
    }

    private List<Map<String, String>> getYears(String start, String end) {
        String clause = "";

        if (StringUtils.isNotBlank(start)) {
            clause += "FILTER (xsd:int(?year) >= " + start + ")";
        }

        if (StringUtils.isNotBlank(end)) {
            clause += "FILTER (xsd:int(?year) <= " + end + ")";
        }

        if (StringUtils.isNotBlank(start) || StringUtils.isNotBlank(end)) {
            return RDFUtils.transformListMap(SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_YEARS.toString(clause), "year"));
        }

        return null;
    }

    @Override
    public List<Map<String, String>> countInstanceFacet(InstanceSearchBean search, String facet) {
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);

        if (isSearchBlank(search)) {
            //Get statistics from temp graph
            if ("bf:provisionActivity/bf:date".equals(facet)) {
                return RDFUtils.transformListMap(SparqlExecution.vQuery(getModel(Constant.GRAPH_TEMP), RdfQuery.RDF_QUERY_FACET_YEAR.toString("time:Instant", year), "uri", "cnt", "facet_text"));
            }

            if ("bf:provisionActivity/bf:place".equals(facet)) {
                return RDFUtils.transformListMap(SparqlExecution.vQuery(getModel(Constant.GRAPH_TEMP), RdfQuery.RDF_QUERY_WORK_FACET.toString("shl:Place"), "s", "cnt", "facet_text"));
            }

            if ("bf:provisionActivity/bf:agent".equals(facet)) {
                return RDFUtils.transformListMap(SparqlExecution.vQuery(getModel(Constant.GRAPH_TEMP), RdfQuery.RDF_QUERY_WORK_FACET.toString("shl:Organization"), "s", "cnt", "facet_text"));
            }
            
            if ("shl:image".equals(facet)) {
                return RDFUtils.transformListMap(SparqlExecution.vQuery(getModel(Constant.GRAPH_TEMP), RdfQuery.RDF_QUERY_FULLTEXT_FACET.toString(facet), "fulltextFlg", "count"));
            }

            return null;
        } else {
            // List<Map<String, String>> year_list = this.getYears(search.getStartYear(), search.getEndYear());
            String clause = MergeParts.instanceSearch(search);
            // clause = clause + MergeParts.instanceYearsSearch(year_list);

            if (facet.equals("bf:provisionActivity/bf:date")) {
                // return RDFUtils.transformListMap(SparqlExecution.vQuery(graph, RdfQuery.RDF_FACET_INSTANCE_YAS.toString(clause, year), "facet_text", "cnt"));
                return RDFUtils.transformListMap(SparqlExecution.vQuery(graph, RdfQuery.RDF_FACET_INSTANCE_YAS.toString(clause,year), "facet_text", "cnt"));
            }

            if (facet.equals("bf:provisionActivity/bf:place")) {
                ArrayList results = SparqlExecution.vQuery(graph, RdfQuery.RDF_FACET_INSTANCE_PLS.toString(clause, facet), "s", "cnt");
                for (int i=0; i<results.size(); i++) {
                    String uri = ((Map) results.get(i)).get("s").toString();
                    ArrayList places = SparqlExecution.rQuery(appConfig.getLocal(), RdfQuery.RDF_QUERY_INSTANCE_REMOTE_PLACE.toString(appConfig.getRemote(), uri), "label");
                    if (places.size() > 0) {
                        ((Map) results.get(i)).put("facet_text", RDFUtils.getValue(((Map) places.get(0)).get("label").toString()));
                    } else {
                        ((Map) results.get(i)).put("facet_text", uri);
                    }
                }
                return RDFUtils.transformListMap(results);
            }
            
            if (facet.equals("bf:provisionActivity/bf:agent")) {
                return RDFUtils.transformListMap(SparqlExecution.vQuery(graph, RdfQuery.RDF_FACET_INSTANCE_PUS.toString(clause, facet), "s", "facet_text", "cnt"));
            }
            
            if (facet.equals("shl:image")) {
                return RDFUtils.transformListMap(SparqlExecution.vQuery(graph, RdfQuery.RDF_FACET_INSTANCE_FTF.toString(clause, facet), "fulltextFlg", "count"));
            }

            return null;
        }
    }

    private boolean isSearchBlank(InstanceSearchBean search) {
        if (StringUtils.isNotBlank(search.getFreetext()) || StringUtils.isNotBlank(search.getTitle()) || StringUtils.isNotBlank(search.getPerson()) || StringUtils.isNotBlank(search.getWork())
                || StringUtils.isNotBlank(search.getStartYear()) || StringUtils.isNotBlank(search.getEndYear()) || StringUtils.isNotBlank(search.getPublisher())
                || StringUtils.isNotBlank(search.getCode()) || StringUtils.isNotBlank(search.getYear()) || StringUtils.isNotBlank(search.getSeries()) 
                || StringUtils.isNotBlank(search.getPlace()) || StringUtils.isNotBlank(search.getShelfMark()) || StringUtils.isNotBlank(search.getFulltextFlg())
                || StringUtils.isNotBlank(search.getSubFreetext()) || StringUtils.isNotBlank(search.getPersonUri())) {
            return false;
        }

        return true;
    }

    @Override
    public List<Map<String, String>> getPlacesInArea(String points, InstanceSearchBean search) {
    	String filter = MergeParts.instanceSearch(search);
    	
    	return RDFUtils.transformListMap(SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_PLACE_IN_INSTANCE.toString(points, filter), "place", "long", "lat", "label", "cnt" ));
    }
    
    @Override
    public List<Map<String, String>> getPlacesInAreaCount(String points, InstanceSearchBean search) {
    	String filter = MergeParts.instanceSearch(search);
    	
    	return RDFUtils.transformListMap(SparqlExecution.vQuery(graph, RdfQuery.RDF_QUERY_PLACE_IN_INSTANCE_COUNT.toString(points, filter), "cnt" ));
    }
}
